package com.bifang.core.gateway.support;

import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bifang.common.annotations.ApiService;
import com.bifang.common.annotations.OpApi;
import com.bifang.common.annotations.SaveOplog;
import com.bifang.common.constant.CacheConst;
import com.bifang.common.constant.enums.common.AuTypeEnum;
import com.bifang.common.constant.enums.common.BoolEnum;
import com.bifang.common.constant.enums.common.NodeTypeEnum;
import com.bifang.common.helper.ApplicationContextHelper;
import com.bifang.common.helper.RedisHelper;
import com.bifang.core.dao.model.SysDict;
import com.bifang.core.dao.model.SysFunction;
import com.bifang.core.dao.service.SysDictService;
import com.bifang.core.dao.service.SysFunctionService;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

/** Api接口扫描器 */
@Component
public class ApiScanner implements CommandLineRunner {

    @Autowired RedisHelper redisHelper;
    @Autowired SysDictService sysDictService;
    @Autowired
    SysFunctionService sysFunctionService;

    @Value("${system.config.update.operationType}")
    private Boolean opFlag;

    @Value("${system.config.update.function}")
    private Boolean funcFlag;

    private static final Logger LOGGER = LoggerFactory.getLogger(ApiScanner.class);

    /** 方法签名拆分正则 */
    private static final Pattern PATTERN =
            Pattern.compile("\\s+(.*)\\s+((.*)\\.(.*))\\((.*)\\)", Pattern.DOTALL);

    /** 参数分隔符 */
    private static final String PARAMS_SEPARATOR = ",";

    /** 统计扫描次数 */
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @Resource private ApiContainer apiContainer;

    @Override
    public void run(String... var1) throws Exception {
        // 扫描所有使用@ApiService注解的类
        Map<String, Object> openApiServiceBeanMap =
                ApplicationContextHelper.getBeansWithAnnotation(ApiService.class);

        if (null == openApiServiceBeanMap || openApiServiceBeanMap.isEmpty()) {
            LOGGER.info("open api service bean map is empty");
            return;
        }

        for (Map.Entry<String, Object> map : openApiServiceBeanMap.entrySet()) {
            Class clzz = map.getValue().getClass();
            ApiService apiService = AnnotationUtils.findAnnotation(clzz, ApiService.class);
            String parentId = "";
            int rate = 0;
            int parentOrderNo = 0;
            if (apiService != null) {
                QueryWrapper<SysFunction> queryWrapper = new QueryWrapper<>();
                queryWrapper
                        .lambda()
                        .eq(SysFunction::getDel, BoolEnum.FALSE.getType())
                        .eq(SysFunction::getFuncCode, apiService.funcCode());
                SysFunction old = sysFunctionService.getOne(queryWrapper);
                SysFunction sysFunctionParent;
                rate = rate + 1;
                parentOrderNo = parentOrderNo + 1;
                if (old != null) {
                    sysFunctionParent = Convert.convert(SysFunction.class, old);
                    rate = sysFunctionParent.getRate();
                } else {
                    sysFunctionParent = new SysFunction();
                    sysFunctionParent.setFuncId(apiService.funcCode());
                    sysFunctionParent.setRate(rate);
                    sysFunctionParent.setOrderNo(parentOrderNo);
                    sysFunctionParent.setNodeType(NodeTypeEnum.node.getType());
                    sysFunctionParent.setParentId("#");
                }
                sysFunctionParent.setAuType(AuTypeEnum.OTHER.getType());
                sysFunctionParent.setPublicFlag(BoolEnum.FALSE.getType());
                sysFunctionParent.setFuncCode(apiService.funcCode());
                sysFunctionParent.setClassPath(map.getKey());
                sysFunctionParent.setFuncTitle(apiService.title());
                sysFunctionParent.setMethod("");
                sysFunctionParent.setStatus(BoolEnum.TRUE.getType());
                sysFunctionParent.setFuncType("");
                sysFunctionParent.insertOrUpdate();
                parentId = sysFunctionParent.getFuncId();
            }
            // 获取扫描类下所有方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clzz);
            int orderNo = 0;
            for (Method method : methods) {
                atomicInteger.incrementAndGet();
                // 找到带有OpenApi 注解的方法
                OpApi opApi = AnnotationUtils.findAnnotation(method, OpApi.class);
                SaveOplog saveOplog = AnnotationUtils.findAnnotation(method, SaveOplog.class);
                if (null == opApi && null == saveOplog) {
                    continue;
                }
                if (saveOplog != null && opFlag) {
                    // 启动节点和其他节点作为业务类型
                    if (saveOplog.auType() != null
                            && (saveOplog.auType().getType().equals(AuTypeEnum.START.getType())
                                    || saveOplog
                                            .auType()
                                            .getType()
                                            .equals(AuTypeEnum.OTHER.getType()))) {
                        QueryWrapper<SysDict> dictQueryWrapper = new QueryWrapper<>();
                        dictQueryWrapper
                                .lambda()
                                .eq(SysDict::getDel, BoolEnum.FALSE.getType())
                                .eq(SysDict::getDictField, "OPERATIONTYPE")
                                .eq(SysDict::getDictKey, saveOplog.operationType());
                        SysDict oldSysDict = sysDictService.getOne(dictQueryWrapper);
                        SysDict sysDict;
                        if (oldSysDict == null) {
                            sysDict = new SysDict();
                            sysDict.setDictField("OPERATIONTYPE");
                            sysDict.setDictValue(saveOplog.operationName());
                            sysDict.setDictKey(saveOplog.operationType());
                            sysDict.setStatus(BoolEnum.TRUE.getType());
                            sysDict.setFixed(BoolEnum.TRUE.getType());
                            sysDict.setFieldName("业务类型");
                            sysDict.setClassify(apiService.funcCode());
                            sysDict.setRemarks(
                                    "业务类型系统根据接口自动生成，有@saveOplog注解并且工作流审核类型为开始节点的方法生成业务类型字典");
                        } else {
                            sysDict = Convert.convert(SysDict.class, oldSysDict);
                            sysDict.setDictValue(saveOplog.operationName());
                        }
                        sysDict.setClassify(opApi.funcCode());
                        sysDictService.saveOrUpdate(sysDict);
                        LOGGER.info(
                                "业务类型加载成功 >> type = {} ,name = {}",
                                saveOplog.operationType(),
                                saveOplog.operationName());
                    }
                }
                if (opApi != null && funcFlag) {
                    orderNo = orderNo + 1;
                    QueryWrapper<SysFunction> queryWrapper = new QueryWrapper<>();
                    queryWrapper
                            .lambda()
                            .eq(SysFunction::getDel, BoolEnum.FALSE.getType())
                            .eq(SysFunction::getFuncCode, opApi.funcCode());
                    SysFunction oldFunction = sysFunctionService.getOne(queryWrapper);
                    SysFunction sysFunction;
                    if (oldFunction != null) {
                        sysFunction = Convert.convert(SysFunction.class, oldFunction);
                    } else {
                        sysFunction = new SysFunction();
                        sysFunction.setFuncId(opApi.funcCode());
                        sysFunction.setRate(rate + 1);
                        sysFunction.setOrderNo(orderNo);
                        sysFunction.setNodeType(NodeTypeEnum.leaf.getType());
                        sysFunction.setLogFlag(BoolEnum.FALSE.getType());
                        if (saveOplog != null) {
                            if (!saveOplog.auType().getType().equals(AuTypeEnum.OTHER.getType())) {
                                sysFunction.setAuditFlag(BoolEnum.TRUE.getType());
                            }
                        }
                        sysFunction.setRollbackFlag(BoolEnum.TRUE.getType());
                        sysFunction.setSpanRollbackFlag(BoolEnum.FALSE.getType());
                    }
                    if (saveOplog != null) {
                        sysFunction.setAuType(saveOplog.auType().getType());
                    } else {
                        sysFunction.setAuType(AuTypeEnum.OTHER.getType());
                    }
                    sysFunction.setPublicFlag(opApi.publicFlag().getType());
                    sysFunction.setFuncCode(opApi.funcCode());
                    sysFunction.setClassPath(map.getKey());
                    sysFunction.setFuncTitle(opApi.title());
                    sysFunction.setMethod(method.getName());
                    sysFunction.setStatus(BoolEnum.TRUE.getType());
                    sysFunction.setFuncType(opApi.funcType().getType());

                    sysFunction.setParentId(parentId);
                    sysFunction.insertOrUpdate();
                }
                if (opApi != null) {
                    // 获取业务参数对象
                    String paramName = getParamName(method);
                    // 组建ApiModel- 放入api容器
                    apiContainer.put(
                            opApi.funcCode(),
                            new ApiModel(map.getKey(), method, paramName, opApi.title()));
                    LOGGER.info(
                            "Api接口加载成功 >> func = {} ,method = {} , desc={}",
                            opApi.funcCode(),
                            method.getName(),
                            opApi.title());
                }
            }
        }
        redisHelper.del(CacheConst.SYS_FUNCTION_FUNCID);
        redisHelper.del(CacheConst.FUNCTION_BY_CODE);
        redisHelper.del(CacheConst.SYS_ALL_FUNCTION_TREE);
        LOGGER.info(
                "Api接口容器加载完毕 >> size = {} loopTimes={}", apiContainer.size(), atomicInteger.get());
    }

    /**
     * 获取业务参数对象
     *
     * @param method
     * @return
     */
    private String getParamName(Method method) {
        ArrayList<String> result = new ArrayList<>();
        final Matcher matcher = PATTERN.matcher(method.toGenericString());
        if (matcher.find()) {
            int groupCount = matcher.groupCount() + 1;
            for (int i = 0; i < groupCount; i++) {
                result.add(matcher.group(i));
            }
        }

        // 获取参数部分
        if (result.size() >= 6) {
            String[] params =
                    StringUtils.splitByWholeSeparatorPreserveAllTokens(
                            result.get(5), PARAMS_SEPARATOR);
            if (params.length >= 1) {
                return params[0];
            }
        }
        return null;
    }
}
